package com.lyj.friend.service.user.serviceImpl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lyj.common.core.constants.CacheConstants;
import com.lyj.common.core.constants.Constants;
import com.lyj.common.core.constants.HttpConstants;
import com.lyj.common.core.domain.LoginUser;
import com.lyj.common.core.domain.R;
import com.lyj.common.core.domain.vo.LoginUserVO;
import com.lyj.common.core.enums.ResultCode;
import com.lyj.common.core.enums.UserIdentity;
import com.lyj.common.core.enums.UserStatus;
import com.lyj.common.core.utils.ThreadLocalUtil;
import com.lyj.friend.MailUtil;
import com.lyj.friend.domain.user.User;
import com.lyj.friend.domain.user.dto.UserDTO;
import com.lyj.friend.domain.user.dto.UserUpdateDTO;
import com.lyj.friend.domain.user.vo.UserVO;
import com.lyj.friend.manager.UserCacheManager;
import com.lyj.friend.mapper.user.UserMapper;
import com.lyj.friend.service.user.UserService;
import com.lyj.redis.service.RedisService;
import com.lyj.security.exception.ServiceException;
import com.lyj.security.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: LIUYANJIAO
 * Date: 2025-09-02
 * Time: 20:57
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private UserCacheManager userCacheManager;

    @Autowired
    private TokenService tokenService;

    @Value("${sms.code-expiration:5}")
    private Long emailCodeExpiration;

    @Value("${sms.send-limit:3}")
    private Integer sendLimit;

    @Value("${jwt.secret}")
    private String secret;

    @Value("${file.oss.downloadUrl}")
    private String downloadUrl;

    @Override
    public boolean sendCode(UserDTO userDTO) {
        //检查邮箱格式
        if(!checkEmail(userDTO.getEmail())){
            throw new ServiceException(ResultCode.FAILED_USER_EMAIL);
        }
        //不可连续恶意发送验证码
            //生成验证码存储键
        String emailCodeKey = getEmailCodeKey(userDTO.getEmail());
            //获得当前存储键的剩余有效时间
        Long expire = redisService.getExpire(emailCodeKey, TimeUnit.SECONDS);
            //存在有效时间并且刚发送不超过60s 抛出错误
        if(expire != null && (emailCodeExpiration*60 - expire)<60){
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }
        //一天一个邮箱只能发送30次验证码上限
        String codeTimeKey = getCodeTimeKey(userDTO.getEmail());
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if(sendTimes != null && sendTimes >= sendLimit){
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }
        //随机生成验证码
        String code = RandomUtil.randomNumbers(6);
        //存储到redis中
        redisService.setCacheObject(emailCodeKey,code,emailCodeExpiration,TimeUnit.MINUTES);
        //发送验证码
        Boolean b = mailUtil.sendSampleMail(userDTO.getEmail(), code);
        if(!b){
            throw new ServiceException(ResultCode.FAILED_SEND_CODE);
        }
        //添加到当天的发送次数记录中
        redisService.increment(codeTimeKey);//计数加一
        //动态处理验证码次数的有效时间
        if (sendTimes == null) {  //说明是当天第一次发起获取验证码的请求
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }
        return true;


    }

    @Override
    public String codeLogin(String email, String code) {
        //首先检查验证码是否正确
        checkCode(email,code);
        //判断用户是否存在
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda()
                .eq(User::getEmail, email));
        if(user == null){
            //添加用户
            user = new User();
            user.setEmail(email);
            user.setStatus(UserStatus.Normal.getValue());
            user.setCreateBy(Constants.SYSTEM_USER_ID);
            userMapper.insert(user);
        }
        //登录
        return tokenService.createToken(user.getUserId(), secret, UserIdentity.ORDINARY.getValue(), user.getNickName(), user.getHeadImage());
    }

    @Override
    public boolean logout(String token) {
        if(StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)){
            token = token.replaceFirst(HttpConstants.PREFIX,StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token,secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        if(StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)){
            token = token.replaceFirst(HttpConstants.PREFIX,StrUtil.EMPTY);
        }
        LoginUser loginUser = tokenService.getLoginUser(token,secret);
        if(loginUser == null){
            return R.fail();
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());
        if(StrUtil.isNotEmpty(loginUser.getHeadImage())){
            loginUserVO.setHeadImage(downloadUrl+loginUser.getHeadImage());
        }
        return R.ok(loginUserVO);
    }

    @Override
    public UserVO detail() {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVo = userCacheManager.getUserById(userId);
        if(userVo == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if(StrUtil.isNotEmpty(userVo.getHeadImage())){
            userVo.setHeadImage(downloadUrl+userVo.getHeadImage());
        }
        return userVo;

    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID,Long.class);
        if(userId == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if(user == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());

        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),ThreadLocalUtil.get(Constants.USER_KEY,String.class));
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if(userId == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if(user == null){
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setHeadImage(headImage);
        //更新用户详细信息缓存
        userCacheManager.refreshUser(user);
        //更新登录用户信息缓存
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY,String.class));
        //更新数据库
        return userMapper.updateById(user);
    }


    /**
     * 检查邮箱格式是否合法
     * @param email 待检查的邮箱字符串
     * @return 合法返回true，否则返回false
     */
    public static boolean checkEmail(String email) {
        // 空值检查
        if (email == null || email.isEmpty()) {
            return false;
        }

        // 邮箱正则表达式
        // 支持常见邮箱格式，包括字母、数字、下划线、点号、连字符等
        String regex = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(email);

        return matcher.matches();
    }
    private String getEmailCodeKey(String email) {
        return CacheConstants.EMAIL_CODE_KEY + email;
    }

    private String getCodeTimeKey(String email) {
        return CacheConstants.CODE_TIME_KEY + email;
    }

    private void checkCode(String email, String code) {
        String emailCodeKey = getEmailCodeKey(email);
        String cacheCode = redisService.getCacheObject(emailCodeKey, String.class);
        if(StrUtil.isEmpty(cacheCode)){
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if(!cacheCode.equals(code)){
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        //验证成功后删除
        redisService.deleteObject(emailCodeKey);
    }
}
